﻿//-----------------------------------------------------------------------
// <copyright>
// This file is subject to the terms and conditions of the Microsoft Public License (MS-PL).
// See http://www.microsoft.com/opensource/licenses.mspx#Ms-PL for more details.
// </copyright>
//----------------------------------------------------------------------- 
using System;
using System.Reflection;

namespace Fulcrum.DispatcherProxies
{
    static class ReflectionHelp
    {
        static public ObsoleteAttribute FindObsoleteAttribute(MemberInfo member)
        {
            object[] attributes = member.GetCustomAttributes(typeof(ObsoleteAttribute), false);

            if (attributes.Length > 0)
            {
                return (ObsoleteAttribute)attributes[0];
            }

            return null;
        }

        public static bool IsOverrideMethod(MethodInfo method)
        {
            //
            // The proxy needs to override some methods from Object to call the inner object version
            //
            if (method.Name.Equals("Equals", StringComparison.InvariantCulture) ||
                method.Name.Equals("GetHashCode", StringComparison.InvariantCulture) ||
                method.Name.Equals("ToString", StringComparison.InvariantCulture))
            {
                return true;
            }
            else
            {
                return false;
            }
        }

        static public bool IsOverridenMethod(MethodInfo method)
        {
            return (method.IsVirtual && ((method.Attributes & MethodAttributes.NewSlot) != MethodAttributes.NewSlot));
        }

        static public Type[] GetParameterTypes(MethodBase method)
        {
            Type[] parameterTypes;

            if (method.GetParameters() == null)
            {
                parameterTypes = new Type[0];
            }
            else
            {
                ParameterInfo[] parms = method.GetParameters();
                parameterTypes = new Type[parms.Length];
                for (int i = 0; i < parms.Length; i++)
                {
                    parameterTypes[i] = parms[i].ParameterType;
                }
            }

            return parameterTypes;
        }

        static public bool IsMethodInAnyBaseType(MethodBase method)
        {
            return InternalIsMethodInAnyBaseType(method, method.DeclaringType);
        }

        static bool InternalIsMethodInAnyBaseType(MethodBase method, Type targetType)
        {
            Type[] parameterTypes = GetParameterTypes(method);  // parameter signature types.
            BindingFlags flags = BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance;

            Type type = targetType.BaseType;

            while (type != null && DispatcherProxyGenerationPolicy.IsProxyableType(type))
            {
                MethodBase baseTypeMethod;

                if (method.IsConstructor)
                {
                    baseTypeMethod = type.GetConstructor(flags, null, parameterTypes, null);
                }
                else
                {
                    baseTypeMethod = type.GetMethod(method.Name, flags, null, CallingConventions.Any, parameterTypes, null);
                }

                // In addition to checking that the baseTypeMethod is not null, we will also have to manually
                // check that the parameters of the returned method/constructor match exactly with the expected
                // parameters.
                //
                // This is needed since GetConstructor/GetMethod don't perform an exact match on parameter types
                if (baseTypeMethod != null && DoMethodParameterTypesMatch(method, baseTypeMethod))
                {
                    return true; // method with same signature found
                }

                type = type.BaseType;
            }

            return false;    // method with same signature not found.
        }

        static bool DoMethodParameterTypesMatch(MethodBase method1, MethodBase method2)
        {
            ParameterInfo[] method1Parameters = method1.GetParameters();
            ParameterInfo[] method2Parameters = method2.GetParameters();

            if (method1Parameters.Length != method2Parameters.Length)
            {
                return false;
            }

            for (int i = 0; i < method1Parameters.Length; i++)
            {
                if (!method1Parameters[i].ParameterType.Equals(method2Parameters[i].ParameterType))
                {
                    return false;
                }
            }

            return true;
        }
    }
}
